package org.lisy.distributed.lock.database;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.concurrent.TimeUnit;

import org.springframework.util.ObjectUtils;

public class DatabaseLock {

	private static String LOCK_KEY = "lockKey";
	private static String REQUEST_ID_1 = "1";
	private static String REQUEST_ID_2 = "2";

	/**
	 * 数据库实现的核心思想:
	 * 1.在数据库中创建一个表，包含方法名(锁标识)等字段
	 * 2.在方法名字段上创建唯一索引
	 * 3.执行某个方法，使用这个方法名向表中插入数据
	 * 4.成功插入则获取锁，执行完成后删除对应的行数据释放锁
	 */
	public static void main(String[] args) {
		Connection conn = DbUtil.getConnection();
		if (conn != null) {
			Statement stmt = null;
			try {
				stmt = conn.createStatement();
				boolean distributedLock = tryGetDistributedLock(stmt, LOCK_KEY, REQUEST_ID_1);
				if (distributedLock) {
					System.out.println(new Timestamp(System.currentTimeMillis()) + " get lock");
				} else {
					System.out.println(new Timestamp(System.currentTimeMillis()) + " not get lock");
					releaseDistributedLock(stmt, LOCK_KEY, null);
					System.out.println(new Timestamp(System.currentTimeMillis()) + " release lock");
					return;
				}
				int count = 0;
				while (true) {
					if (count == 5) {
						releaseDistributedLock(stmt, LOCK_KEY, REQUEST_ID_1);
						System.out.println(new Timestamp(System.currentTimeMillis()) + " release lock");
					}
					if (tryGetDistributedLock(stmt, LOCK_KEY, REQUEST_ID_2)) {
						System.out.println(new Timestamp(System.currentTimeMillis()) + " lock expire");
						break;
					}
					System.out.println(new Timestamp(System.currentTimeMillis()) + " lock effective, wait 1s");
					TimeUnit.SECONDS.sleep(1);
					count++;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			DbUtil.closeConnection();
			System.out.println(new Timestamp(System.currentTimeMillis()) + " close connection");
		}
	}

	/**
	 * 尝试获取锁
	 * 
	 * @param stmt
	 * @param lockKey 锁标识
	 * @param desc 备注信息
	 * @return
	 * @throws InterruptedException
	 */
	private static boolean tryGetDistributedLock(Statement stmt, String lockKey, String desc) throws InterruptedException {
		String sql = "insert into lock_tab(lock_name, desc_val) values ('" + lockKey + "', '" + desc + "')";
		int count = 0;
		while (count < 2) {
			try {
				int executeUpdate = stmt.executeUpdate(sql);
				if (executeUpdate == 1) {
					return true;
				}
			} catch (SQLException e) {
				TimeUnit.MILLISECONDS.sleep(10);
				count++;
			}
		}
		return false;
	}

	/**
	 * 释放锁
	 * 
	 * @param stmt
	 * @param lockKey 锁标识
	 * @param desc 备注信息
	 * @return
	 */
	private static boolean releaseDistributedLock(Statement stmt, String lockKey, String desc) {
		String sql = "delete from lock_tab where lock_name ='" + lockKey + "'";
		if (!ObjectUtils.isEmpty(desc)) {
			sql += " and desc_val ='" + desc + "'";
		}
		try {
			int executeUpdate = stmt.executeUpdate(sql);
			if (executeUpdate == 1) {
				return true;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}

}
